home *** CD-ROM | disk | FTP | other *** search
/ PC World 2008 April / PCWorld_2008-04_cd.bin / v cisle / ozo / zotero-1.0.3.xpi / components / zotero-autocomplete.js next >
Text File  |  2007-03-16  |  10KB  |  341 lines

  1. /*
  2.     ***** BEGIN LICENSE BLOCK *****
  3.     
  4.     Copyright (c) 2006  Center for History and New Media
  5.                         George Mason University, Fairfax, Virginia, USA
  6.                         http://chnm.gmu.edu
  7.     
  8.     Licensed under the Educational Community License, Version 1.0 (the "License");
  9.     you may not use this file except in compliance with the License.
  10.     You may obtain a copy of the License at
  11.     
  12.     http://www.opensource.org/licenses/ecl1.php
  13.     
  14.     Unless required by applicable law or agreed to in writing, software
  15.     distributed under the License is distributed on an "AS IS" BASIS,
  16.     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17.     See the License for the specific language governing permissions and
  18.     limitations under the License.
  19.     
  20.     ***** END LICENSE BLOCK *****
  21. */
  22.  
  23. const ZOTERO_AC_CONTRACTID = '@mozilla.org/autocomplete/search;1?name=zotero';
  24. const ZOTERO_AC_CLASSNAME = 'Zotero AutoComplete';
  25. const ZOTERO_AC_CID = Components.ID('{06a2ed11-d0a4-4ff0-a56f-a44545eee6ea}');
  26.  
  27. const Cc = Components.classes;
  28. const Ci = Components.interfaces;
  29. const Cr = Components.results;
  30.  
  31.  
  32. /*
  33.  * Implements nsIAutoCompleteResult
  34.  */
  35. function ZoteroAutoCompleteResult(searchString, searchResult, defaultIndex,
  36.     errorDescription, results, comments){
  37.         this._searchString = searchString;
  38.         this._searchResult = searchResult;
  39.         this._defaultIndex = defaultIndex;
  40.         this._errorDescription = errorDescription;
  41.         this._results = results;
  42.         this._comments = comments;
  43. }
  44.  
  45.  
  46. ZoteroAutoCompleteResult.prototype = {
  47.     _searchString: "",
  48.     _searchResult: 0,
  49.     _defaultIndex: 0,
  50.     _errorDescription: "",
  51.     _results: [],
  52.     _comments: [],
  53.     get searchString(){ return this._searchString; },
  54.     get searchResult(){ return this._searchResult; },
  55.     get defaultIndex(){ return this._defaultIndex; },
  56.     get errorDescription(){ return this._errorDescription; },
  57.     get matchCount(){ return this._results.length; }
  58. }
  59.  
  60.  
  61. ZoteroAutoCompleteResult.prototype.getCommentAt = function(index){
  62.     return this._comments[index];
  63. }
  64.  
  65.  
  66. ZoteroAutoCompleteResult.prototype.getStyleAt = function(index){
  67.     return null;
  68. }
  69.  
  70.  
  71. ZoteroAutoCompleteResult.prototype.getValueAt = function(index){
  72.     return this._results[index];
  73. }
  74.  
  75.  
  76. ZoteroAutoCompleteResult.prototype.removeValueAt = function(index){
  77.     this._results.splice(index, 1);
  78.     this._comments.splice(index, 1);
  79. }
  80.  
  81.  
  82. ZoteroAutoCompleteResult.prototype.QueryInterface = function(iid){
  83.     if (!iid.equals(Ci.nsIAutoCompleteResult) &&
  84.         !iid.equals(Ci.nsISupports)){
  85.             throw Cr.NS_ERROR_NO_INTERFACE;
  86.         }
  87.     return this;
  88. }
  89.  
  90.  
  91.  
  92.  
  93. /*
  94.  * Implements nsIAutoCompleteSearch
  95.  */
  96. function ZoteroAutoComplete(){
  97.     // Get the Zotero object
  98.     this._zotero = Components.classes["@zotero.org/Zotero;1"]
  99.                 .getService(Components.interfaces.nsISupports)
  100.                 .wrappedJSObject;
  101. }
  102.  
  103.  
  104. ZoteroAutoComplete.prototype.startSearch = function(searchString, searchParam,
  105.         previousResult, listener){
  106.     
  107.     this.stopSearch();
  108.     
  109.     /*
  110.     this._zotero.debug("Starting autocomplete search of type '"
  111.         + searchParam + "'" + " with string '" + searchString + "'");
  112.     */
  113.     
  114.     var results = [];
  115.     var comments = [];
  116.     
  117.     // Allow extra parameters to be passed in
  118.     var pos = searchParam.indexOf('/');
  119.     if (pos!=-1){
  120.         var extra = searchParam.substr(pos + 1);
  121.         var searchParam = searchParam.substr(0, pos);
  122.     }
  123.     
  124.     var searchParts = searchParam.split('-');
  125.     searchParam = searchParts[0];
  126.     
  127.     switch (searchParam){
  128.         case '':
  129.             break;
  130.         
  131.         case 'tag':
  132.             var sql = "SELECT tag FROM tags WHERE tag LIKE ?";
  133.             var sqlParams = [searchString + '%'];
  134.             if (extra){
  135.                 sql += " AND tagID NOT IN (SELECT tagID FROM itemTags WHERE "
  136.                     + "itemID = ?)";
  137.                 sqlParams.push(extra);
  138.             }
  139.             sql += " ORDER BY tag";
  140.             var results = this._zotero.DB.columnQuery(sql, sqlParams);
  141.             break;
  142.         
  143.         case 'creator':
  144.             // Valid fieldMode values:
  145.             //         0 == search two-field creators
  146.             //         1 == search single-field creators
  147.             //         2 == search both
  148.             var [fieldMode, itemID] = extra.split('-');
  149.             
  150.             if (fieldMode==2)
  151.             {
  152.                 var sql = "SELECT DISTINCT CASE fieldMode WHEN 1 THEN lastName "
  153.                     + "WHEN 0 THEN firstName || ' ' || lastName END AS name "
  154.                     + "FROM creators WHERE CASE fieldMode "
  155.                     + "WHEN 1 THEN lastName "
  156.                     + "WHEN 0 THEN firstName || ' ' || lastName END "
  157.                     + "LIKE ? ORDER BY name";
  158.                 var sqlParams = searchString + '%';
  159.             }
  160.             else
  161.             {
  162.                 var sql = "SELECT DISTINCT ";
  163.                 if (fieldMode==1){
  164.                     sql += "lastName AS name, creatorID || '-1' AS creatorID";
  165.                 }
  166.                 // Retrieve the matches in the specified field
  167.                 // as well as any full names using the name
  168.                 //
  169.                 // e.g. "Shakespeare" and "Shakespeare, William"
  170.                 //
  171.                 // creatorID is in the format "12345-1" or "12345-2",
  172.                 //         - 1 means the row uses only the specified field
  173.                 //         - 2 means it uses both
  174.                 else {
  175.                     sql += "CASE WHEN firstName='' OR firstName IS NULL THEN lastName "
  176.                         + "ELSE lastName || ', ' || firstName END AS name, "
  177.                         + "creatorID || '-' || CASE "
  178.                         + "WHEN (firstName = '' OR firstName IS NULL) THEN 1 "
  179.                         + "ELSE 2 END AS creatorID";
  180.                 }
  181.                 
  182.                 var fromSQL = " FROM creators WHERE " + searchParts[2]
  183.                     + " LIKE ?1 " + "AND fieldMode=?2";
  184.                 var sqlParams = [searchString + '%', parseInt(fieldMode)];
  185.                 if (itemID){
  186.                     fromSQL += " AND creatorID NOT IN (SELECT creatorID FROM "
  187.                         + "itemCreators WHERE itemID=?3)";
  188.                     sqlParams.push(itemID);
  189.                 }
  190.                 
  191.                 sql += fromSQL;
  192.                 
  193.                 // If double-field mode, include matches for just this field
  194.                 // as well (i.e. "Shakespeare"), and group to collapse repeats
  195.                 if (fieldMode!=1){
  196.                     sql = "SELECT * FROM (" + sql + " UNION SELECT DISTINCT "
  197.                         + searchParts[2] + " AS name, creatorID || '-1' AS creatorID"
  198.                         + fromSQL + ") GROUP BY name";
  199.                 }
  200.                 
  201.                 sql += " ORDER BY name";
  202.             }
  203.             
  204.             var rows = this._zotero.DB.query(sql, sqlParams);
  205.             for each(var row in rows){
  206.                 results.push(row['name']);
  207.                 comments.push(row['creatorID'])
  208.             }
  209.             break;
  210.         
  211.         case 'dateModified':
  212.         case 'dateAdded':
  213.             var sql = "SELECT DISTINCT DATE(" + searchParam + ", 'localtime') FROM items "
  214.                 + "WHERE " + searchParam + " LIKE ? ORDER BY " + searchParam;
  215.             var results = this._zotero.DB.columnQuery(sql, searchString + '%');
  216.             break;
  217.             
  218.         case 'accessDate':
  219.             var fieldID = this._zotero.ItemFields.getID('accessDate');
  220.             
  221.             var sql = "SELECT DISTINCT DATE(value, 'localtime') FROM itemData "
  222.                 + "WHERE fieldID=? AND value LIKE ? ORDER BY value";
  223.             var results = this._zotero.DB.columnQuery(sql, [fieldID, searchString + '%']);
  224.             break;
  225.         
  226.         default:
  227.             var sql = "SELECT fieldID FROM fields WHERE fieldName=?";
  228.             var fieldID = this._zotero.DB.valueQuery(sql, {string:searchParam});
  229.             
  230.             if (!fieldID){
  231.                 this._zotero.debug("'" + searchParam + "' is not a valid autocomplete scope", 1);
  232.                 var results = [];
  233.                 var resultCode = Ci.nsIAutoCompleteResult.RESULT_IGNORED;
  234.                 break;
  235.             }
  236.             
  237.             // We don't use date autocomplete anywhere, but if we're not
  238.             // disallowing it altogether, we should at least do it right and
  239.             // use the user part of the multipart field
  240.             var valueField = searchParam=='date' ? 'SUBSTR(value, 12, 100)' : 'value';
  241.             
  242.             var sql = "SELECT DISTINCT " + valueField
  243.                 + " FROM itemData NATURAL JOIN itemDataValues "
  244.                 + "WHERE fieldID=?1 AND " + valueField
  245.                 + " LIKE ?2 "
  246.             
  247.             var sqlParams = [fieldID, searchString + '%'];
  248.             if (extra){
  249.                 sql += "AND value NOT IN (SELECT value FROM itemData "
  250.                     + "NATURAL JOIN itemDataValues WHERE fieldID=?1 AND itemID=?3) ";
  251.                 sqlParams.push(extra);
  252.             }
  253.             sql += "ORDER BY value";
  254.             var results = this._zotero.DB.columnQuery(sql, sqlParams);
  255.     }
  256.     
  257.     if (!results || !results.length){
  258.         var results = [];
  259.         var resultCode = Ci.nsIAutoCompleteResult.RESULT_NOMATCH;
  260.     }
  261.     else if (typeof resultCode == 'undefined'){
  262.         var resultCode = Ci.nsIAutoCompleteResult.RESULT_SUCCESS;
  263.     }
  264.     
  265.     var result = new ZoteroAutoCompleteResult(searchString,
  266.         resultCode, 0, "", results, comments);
  267.     
  268.     listener.onSearchResult(this, result);
  269. }
  270.  
  271.  
  272. ZoteroAutoComplete.prototype.stopSearch = function(){
  273.     //this._zotero.debug('Stopping autocomplete search');
  274. }
  275.  
  276.  
  277. ZoteroAutoComplete.prototype.QueryInterface = function(iid){
  278.     if (!iid.equals(Ci.nsIAutoCompleteSearch) &&
  279.         !iid.equals(Ci.nsIAutoCompleteObserver) &&
  280.         !iid.equals(Ci.nsISupports)){
  281.           throw Cr.NS_ERROR_NO_INTERFACE;
  282.     }
  283.     return this;
  284. }
  285.  
  286.  
  287.  
  288. //
  289. // XPCOM goop
  290. //
  291. var ZoteroAutoCompleteFactory = {
  292.     createInstance: function(outer, iid){
  293.         if (outer != null){
  294.             throw Components.results.NS_ERROR_NO_AGGREGATION;
  295.         }
  296.         return new ZoteroAutoComplete().QueryInterface(iid);
  297.     }
  298. };
  299.  
  300.  
  301. var ZoteroAutoCompleteModule = {
  302.     _firstTime: true,
  303.     
  304.     registerSelf: function(compMgr, fileSpec, location, type){
  305.         if (!this._firstTime){
  306.             throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
  307.         }
  308.         this._firstTime = false;
  309.         
  310.         compMgr =
  311.             compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
  312.         
  313.         compMgr.registerFactoryLocation(ZOTERO_AC_CID,
  314.                                         ZOTERO_AC_CLASSNAME,
  315.                                         ZOTERO_AC_CONTRACTID,
  316.                                         fileSpec,
  317.                                         location,
  318.                                         type);
  319.     },
  320.     
  321.     unregisterSelf: function(compMgr, location, type){
  322.         compMgr =
  323.             compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
  324.         compMgr.unregisterFactoryLocation(ZOTERO_AC_CID, location);
  325.     },
  326.     
  327.     getClassObject: function(compMgr, cid, iid){
  328.         if (!cid.equals(ZOTERO_AC_CID)){
  329.             throw Components.results.NS_ERROR_NO_INTERFACE;
  330.         }
  331.         if (!iid.equals(Components.interfaces.nsIFactory)){
  332.             throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  333.         }
  334.         return ZoteroAutoCompleteFactory;
  335.     },
  336.     
  337.     canUnload: function(compMgr){ return true; }
  338. };
  339.  
  340. function NSGetModule(comMgr, fileSpec){ return ZoteroAutoCompleteModule; }
  341.